/** 内置API */

/**
@desc 运行哈希谜题
@param puzzle:String 谜题
@param solution:String 答案
*/
const BRUN = (puzzle,solution) => SHELL(`brun "${puzzle}" "${solution}"`)


const RUN = str => SHELL(`run '${str}'`)

const SHA256 = str => RUN(`(sha256 ${str})`)

/**
 开启debug模式
*/
const DEBUG = () => DEBUG_FLAG = true

/**
  删除js
*/
const OVER_EVENT = (e)=> {

	if(e){
		INFO(e)
	}

	if(DEBUG_FLAG)
		OUT_LOG(true)

	if(e)
		throw e

	if(__isDeploy__ > 1){
		console.log(`deploy time:${__isDeploy__}`)
		__isDeploy__--;
		__runWrapFunction_()
	}else{
		if(!DEBUG_FLAG){
			TEMP_FILES.forEach(f => {
				fs.existsSync(f) && fs.rmSync(f);
			})
		}
		COIN_CHANGE_INFO()
	}

}	

//定义全局变量，用于#！clsp文件异常退出使用
process.on('uncaughtException', function(err) {
	ERROR(`异常退出:${err}`)
	OVER_EVENT()
});


/** clsp命令 */

/**
@desc 断言方法
*/
const ASSERT = (title,condition) => {
	if(!condition)
		ERROR(title)
}

const ASSERT_HAS = (result,hasCotent) => {
	if(result.indexOf(hasCotent) === -1){
		INFO("断言错误退出")
		ERROR(result)
	}
}


/**
@desc将拼图哈希转成地址
@param puzzlehash:String 拼图的哈希值
@param prefix:String 地址前缀 默认txch
*/
const ADDRESS = (puzzleHash,prefix='txch') => SHELL(`cdv encode ${puzzleHash} --prefix ${prefix}`)



const CURRY = (puzzleFile,params,extInstruct="")  => {
	const paramStr = params.reduce((s,v)=> s+=` -a ${v}`,"")
	return SHELL(`cdv clsp curry ${puzzleFile} ${paramStr} ${extInstruct}`)
}


/**
@desc 生成拼图hash
@param params:[Object] curry参数
*/
const TREE_HASH = (puzzleFile,params) => CURRY(puzzleFile,params,"--treehash")


/**
@desc 生成拼图hash
@param params:[Object] curry参数
*/
const PUZZLE_REVEAL = (puzzleFile,params) => CURRY(puzzleFile,params,"-x")


/**
@desc 生成一个拼图的所有信息
@param puzzleFile:String 拼图文件路劲
@param params:[Object] curry参数
@param prefix:String 地址前缀 默认txch
*/
const PUZZLE_RESOLVE = (puzzleFile,params) => {
	const curry = CURRY(puzzleFile,params)
	const treehash = TREE_HASH(puzzleFile,params)
	const address = ADDRESS(treehash,XCH_ADDRESS_PREFIX)
	const puzzle_reveal = PUZZLE_REVEAL(puzzleFile,params)
	return {treehash,address,puzzle_reveal,curry}
}


const DEPLOY_PUZZLE = (fingerprint,amount,address)=> {
	return SHELL(`chia wallet send -a ${MOJO_TO_XCH(amount)} -t ${address} -f ${fingerprint}`)
}

const TRANSACTION_STATE = (fingerprint,transactionId,saveLog=true) => SHELL(`chia wallet get_transaction -f ${fingerprint} -tx ${transactionId} `,saveLog)

const COIN_RECORDS_HASH = hash => JSON.parse(SHELL(`cdv rpc coinrecords  --by puzzlehash ${hash}`))

const WAIT = s => new Promise((r) => setTimeout(r,s*1000))

const OPC = solution => SHELL(`opc ${JSON.stringify(solution)}`)

const PUSH_TX = fileName => SHELL(`cdv rpc pushtx ${fileName}`)

const DELETE_UNCONFIRMED_TRANSACTION = fg => SHELL(`chia wallet delete_unconfirmed_transactions -f ${fg}`)

const DEPLOY = (()=>{

	let addTime=0;

	return (count)=>{

		if(addTime++ == 0)
			__isDeploy__=count;

	}

})();

const __mark_coin = [];

const SHOW_PUZZLE_HASH_INFO = (puzzleHash,amount=null,title='') => {

	let coinlist = new PuzzleCurryOperate({treehash:puzzleHash}).getCoins();

	if(amount)
		coinlist = coinlist.filter(v => v.getAmount() == amount);

	INFO(`@mark puzzle_hash:${puzzleHash} ${amount?"amount:"+amount:""}---`)

	const totalAmount = coinlist.map(v => v.getAmount()).sum();

	const spentCoin = coinlist.filter(c => c.isSpent())

	const spentAmount = spentCoin.map(v => v.getAmount()).sum();

	const json = {
		title,
		"puzzle_hash":puzzleHash,
		"search_amount":amount,
		"total_size":coinlist.length,
		"spent_size":spentCoin.length,
		"unspent_size":coinlist.length - spentCoin.length,
		"spent_amount":spentAmount,
		"un_spent_amount":totalAmount-spentAmount,
		"spent_amount_xch":MOJO_TO_XCH(spentAmount),
		"un_spent_amount_xch":MOJO_TO_XCH(totalAmount-spentAmount)
	}

	__mark_coin.push(json)

	return json;
}

const COIN_CHANGE_INFO = ()=>{

	if(__mark_coin.length==0)
		return;

	const operateCoin = [...__mark_coin]

	const convertText = v => {
		if(v == 0)
				return v;
		return v > 0 ? "+"+v:"-"+v;
	}

	const changeInfo = (newJson,oldJson,attrs) => {
		const result = {...oldJson};
		attrs.forEach(a => {
			result[a] = convertText(newJson[a] - oldJson[a])+`      (${oldJson[a]} -> ${newJson[a]})`;
		})
		return result;
	}

	operateCoin.forEach(coinJson => {

		const {title,puzzle_hash,search_amount} = coinJson;
		const newJson = SHOW_PUZZLE_HASH_INFO(puzzle_hash,search_amount)

		__mark_coin.pop()

		INFO(`@SUMMARY:  puzzle_hash:${puzzle_hash}  search_amount:${search_amount||""}`)

		const changeResult = changeInfo(newJson,coinJson,
			['total_size','spent_size','unspent_size','spent_amount','un_spent_amount','spent_amount_xch','un_spent_amount_xch'])

		INFO(JSON.stringify(changeResult,null,2))
	})
}

const SHOW_BALANCE = (puzzleHash)=>{
	const coins = new PuzzleCurryOperate({treehash:puzzleHash}).getUnSpentCoins();
	const sum = coins.map(v => v.getAmount()).sum();
	const data = {puzzleHash,sum}
	INFO(JSON.stringify(data))
	return {...data.coins};
}